package org.jay.angla.device;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.jay.angla.ble.BluetoothLeService;
import org.jay.angla.utils.Contants;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

@SuppressLint("NewApi")
public class OnlabUVDevice {

	private final String TAG = "OnLabUV";
	private static OnlabUVDevice instance = null;
	static {
		instance = new OnlabUVDevice();
	}

	private OnlabUVDevice() {

	}

	public static OnlabUVDevice getInstance() {
		return instance;
	}

	private Context mContext;
	private BluetoothLeService mBluetoothLeService;
	private BluetoothAdapter mBluetoothAdapter;
	private String bleAddress = null;
	private boolean isBleConnected = false;
	private Handler mBleHandler;
	private static final long SCAN_PERIOD = 10000;
	private final String BLE_HEAD = "CPLB";
	private Handler mHandler;

	public static final int DEVICE_CONNECT_STATE = 800;
	public static final int DEVICE_CONNECT_STATE_CONNECTED = 801;
	public static final int DEVICE_CONNECT_STATE_CONNECTING = 802;
	public static final int DEVICE_CONNECT_STATE_DISCONNECTED = 803;
	public static final int DEVICE_CONNECT_STATE_TIMEOUT = 804;

	public static final int DEVICE_MESSAGE_COMMING = 900;
	public static final int DEVICE_SCANNING = 901;
	public static final int DEVICE_MESSAGE_DATA = 999;

	private byte[] inData;
	private int inLength;
	private boolean done = false;
	private boolean isBusy = false;
	private boolean registed = false;

	private final ServiceConnection mBleServiceConnection = new ServiceConnection() {

		@Override
		public void onServiceConnected(ComponentName componentName,
				IBinder service) {
			mBluetoothLeService = ((BluetoothLeService.LocalBinder) service)
					.getService();

			if (!mBluetoothLeService.initialize()) {
				System.out.println("Unable to initialize Bluetooth");
			}
			// Automatically connects to the device upon successful start-up
			// initialization.
			// mBluetoothLeService.connect(bleAddress);
		}

		@Override
		public void onServiceDisconnected(ComponentName componentName) {
			mBluetoothLeService = null;
		}
	};

	private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

		@Override
		public void onLeScan(final BluetoothDevice device, int rssi,
				byte[] scanRecord) {
			((Activity) mContext).runOnUiThread(new Runnable() {
				@Override
				public void run() {
					final String deviceName = device.getName();
					final String deviceAddress = device.getAddress();

					Bundle bundle = new Bundle();
					bundle.putString("DEVICE_NAME", deviceName);
					bundle.putString("DEVICE_ADDRESS", deviceAddress);
					Message msg = mHandler.obtainMessage(DEVICE_SCANNING, -1,
							-1, -1);
					msg.setData(bundle);
					mHandler.sendMessage(msg);
					/*
					 * if (deviceName != null && deviceName.length() > 0) {
					 * 
					 * if (deviceName.startsWith(BLE_HEAD)) { if
					 * (!isBleConnected) { bleAddress = device.getAddress(); if
					 * (mBluetoothLeService != null) {
					 * mBluetoothLeService.connect(bleAddress); } } } }
					 */
				}
			});
		}
	};

	public void scanLeDevice(final boolean enable) {

		if (enable) {
			// Stops scanning after a pre-defined scan period.
			mBleHandler.postDelayed(new Runnable() {
				@Override
				public void run() {
					if (!isBleConnected) {
						mBluetoothAdapter.stopLeScan(mLeScanCallback);
						// mHandler.obtainMessage(OBD_CONNECT_STATE,
						// OBD_CONNECT_STATE_TIMEOUT, -1).sendToTarget();
					}
				}
			}, SCAN_PERIOD);

			mBluetoothAdapter.startLeScan(mLeScanCallback);
		} else {
			mBluetoothAdapter.stopLeScan(mLeScanCallback);
		}
	}

	private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			final String action = intent.getAction();
			if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
				isBleConnected = true;
				mHandler.obtainMessage(DEVICE_CONNECT_STATE,
						DEVICE_CONNECT_STATE_CONNECTED, -1).sendToTarget();
			} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED
					.equals(action)) {
				isBleConnected = false;
				mHandler.obtainMessage(DEVICE_CONNECT_STATE,
						DEVICE_CONNECT_STATE_DISCONNECTED, -1).sendToTarget();
			} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED
					.equals(action)) {
			} else if (BluetoothLeService.ACTION_DEVICE_FIND.equals(action)) {
				// send connect\r
				connectDevice();
			} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
				// process data
				byte data[] = intent
						.getByteArrayExtra(BluetoothLeService.EXTRA_DATA);
				if (handleData(data)) {
					processData();
					resetData();
				}

			}
		}
	};

	private static IntentFilter makeGattUpdateIntentFilter() {
		final IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
		intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
		intentFilter
				.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
		intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
		intentFilter.addAction(BluetoothLeService.ACTION_DEVICE_FIND);
		intentFilter.addAction(BluetoothLeService.EXTRA_DATA);

		return intentFilter;
	}

	private boolean adxl = false;

	private synchronized boolean handleData(byte[] d) {
		int length = d.length;
		for (int i = 0; i < length; i++) {
			if (d[i] == 0x3e) {
				if (adxl == true) {
					done = true;
					this.notifyAll();
				} else {
					adxl = true;
				}
				Log.d(Contants.TAG, "done");
				return true;
			}
			inData[inLength++] = d[i];
		}

		return false;
	}

	private void processData() {
		Bundle bundle = new Bundle();
		byte[] buf = new byte[inLength];
		for (int i = 0; i < inLength; i++) {
			buf[i] = inData[i];
		}
		bundle.putByteArray("RECV_DATA", buf);
		Message msg = mHandler.obtainMessage(DEVICE_MESSAGE_COMMING,
				DEVICE_MESSAGE_DATA, -1, -1);
		msg.setData(bundle);
		mHandler.sendMessage(msg);
	}

	private void resetData() {
		inLength = 0;
		for (int i = 0; i < inLength; i++) {
			inData[i] = 0x00;
		}
	}

	public void init(Context context) {
		Log.d(Contants.TAG, "ble init");
		mContext = context;
		mBleHandler = new Handler();
		final BluetoothManager bluetoothManager = (BluetoothManager) mContext
				.getSystemService(Context.BLUETOOTH_SERVICE);
		mBluetoothAdapter = bluetoothManager.getAdapter();
		Intent gattServiceIntentFilter = new Intent(mContext,
				BluetoothLeService.class);
		mContext.bindService(gattServiceIntentFilter, mBleServiceConnection,
				Context.BIND_AUTO_CREATE);

		inData = new byte[1000];
		for (int i = 0; i < 1000; i++) {
			inData[i] = 0x00;
		}
		inLength = 1000;
		resetData();
	}

	public void register(Handler handler) {
		Log.d(Contants.TAG, "ble register");
		mContext.registerReceiver(mGattUpdateReceiver,
				makeGattUpdateIntentFilter());
		mHandler = handler;
		registed = true;
	}

	public void unregister() {
		Log.d(Contants.TAG, "ble unregister");
		registed = false;
		mContext.unregisterReceiver(mGattUpdateReceiver);
	}

	public void release() {
		Log.d(Contants.TAG, "ble release");
		mBluetoothAdapter.stopLeScan(mLeScanCallback);
		mBluetoothLeService.disconnect();
		mContext.unbindService(mBleServiceConnection);
	}

	public void connect(String address) {
		if (isBleConnected) {
			mHandler.obtainMessage(DEVICE_CONNECT_STATE,
					DEVICE_CONNECT_STATE_CONNECTED, -1).sendToTarget();
			return;
		}
		isBleConnected = false;
		// scanLeDevice(true);
		mHandler.obtainMessage(DEVICE_CONNECT_STATE,
				DEVICE_CONNECT_STATE_CONNECTING, -1).sendToTarget();
		if (!mBluetoothLeService.initialize()) {
			((Activity) mContext).finish();
		}
		mBluetoothLeService.connect(address);
		bleAddress = address;
	}

	public String getAddress() {
		return bleAddress;
	}

	public void disconnect() {
		mBluetoothLeService.disconnect();
	}

	public boolean isBusy() {
		return isBusy;
	}

	public void connectDevice() {
		Log.d(Contants.TAG, "connect device");
		if (!registed)
			return;
		isBusy = true;

		if (isBleConnected()) {
			mBluetoothLeService.send("connect\r");

			synchronized (this) {
				try {
					Log.d(Contants.TAG, "sleep...");
					this.wait(500);
				} catch (InterruptedException e) {
					// TODO: handle exception
					e.printStackTrace();
				}
			}

			done = false;
		}
		isBusy = false;
	}

	public void getEnergy() {
		Log.d(Contants.TAG, "getEnergy");
		if (!registed)
			return;
		isBusy = true;

		if (isBleConnected()) {
			mBluetoothLeService.send("ge 10\r");
			while (!done) {
				synchronized (this) {
					try {
						Log.d(Contants.TAG, "sleep...");
						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}

			}
			done = false;
		}
		isBusy = false;
	}

	public void getWaveLength() {
		Log.d(Contants.TAG, "getWavelength");
		if (!registed)
			return;
		isBusy = true;

		if (isBleConnected()) {
			mBluetoothLeService.send("getwl\r");
			while (!done) {
				synchronized (this) {
					try {
						Log.d(Contants.TAG, "sleep...");
						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}

			}
			done = false;
		}
		isBusy = false;
	}

	public void getDark() {
		Log.d(Contants.TAG, "getDark");
		if (!registed)
			return;
		isBusy = true;

		if (isBleConnected()) {
			mBluetoothLeService.send("getdark\r");
			while (!done) {
				synchronized (this) {
					try {
						Log.d(Contants.TAG, "sleep...");
						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}

			}
			done = false;
		}

		isBusy = false;
	}

	public void getA() {
		Log.d(Contants.TAG, "get A");
		if (!registed)
			return;
		isBusy = true;

		if (isBleConnected()) {
			mBluetoothLeService.send("ga\r");
			while (!done) {
				synchronized (this) {
					try {
						Log.d(Contants.TAG, "sleep...");
						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}

			}
			done = false;
		}
		isBusy = false;
	}

	public synchronized void reZero() {
		Log.d(Contants.TAG, "rezero");
		if (!registed)
			return;
		isBusy = true;
		if (isBleConnected()) {
			mBluetoothLeService.send("rezero\r");
			while (!done) {
				synchronized (this) {
					try {
						Log.d(Contants.TAG, "sleep...");
						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}

			}
			done = false;
		}

		isBusy = false;
	}

	public void reSetDark() {
		Log.d(Contants.TAG, "resetdark");
		if (!registed)
			return;
		isBusy = true;
		if (isBleConnected()) {
			mBluetoothLeService.send("resetdark\r");
			while (!done) {
				synchronized (this) {
					try {
						Log.d(Contants.TAG, "sleep...");
						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}
			}
			done = false;
		}
		isBusy = false;
	}

	public void setWavelength(float wl) {
		Log.d(Contants.TAG, "setWL " + wl);
		if (!registed)
			return;
		isBusy = true;
		if (isBleConnected()) {
			mBluetoothLeService.send("swl " + wl + "\r");
			while (!done) {
				synchronized (this) {
					try {
						Log.d(Contants.TAG, "sleep...");
						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}
			}
			done = false;
		}
		isBusy = false;
	}

	public void setA(int sa) {
		Log.d(Contants.TAG, "setA " + sa);
		if (!registed)
			return;
		isBusy = true;
		if (isBleConnected()) {
			mBluetoothLeService.send("sa " + sa + "\r");
			while (!done) {
				synchronized (this) {
					try {
						Log.d(Contants.TAG, "sleep...");
						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}
			}
			done = false;
		}
		isBusy = false;
	}

	public void getRealEnergy(float wl, int sa) {
		setWavelength(wl);
		setA(sa);
	}

	public void quit() {
		Log.d(Contants.TAG, "quit");
		if (isBleConnected()) {
			mBluetoothLeService.send("quit\r");
		}
	}

	public boolean isBleConnected() {
		return isBleConnected;
	}

	public static final int CMD_CONNECT = 101;
	public static final int CMD_GET_ENERGY = 102;
	public static final int CMD_GET_WAVELENGTH = 103;
	public static final int CMD_GET_DARK_CURRENT = 104;
	public static final int CMD_GET_A = 105;
	public static final int CMD_REZERO = 106;
	public static final int CMD_RESET_DARK = 107;
	public static final int CMD_SET_WAVELENGTH = 108;
	public static final int CMD_SET_A = 109;

}
